﻿using System;
using System.Linq;
using FireStarter.Base;
using FireStarter.Base.BusinessObjectMerger;
using Firestarter.DB.Schema.BO;
using FireStarterModeller.BO;
using Habanero.Base;
using Habanero.BO;
using Habanero.BO.ClassDefinition;
using Habanero.Smooth;
using Habanero.Testability;
using NUnit.Framework;

namespace Firestarter.Test.Base.BusinessObjectMerger
{
    [TestFixture]
    public class TestSolutionTraverser
    {
        [SetUp]
        public void SetupTest()
        {
            //Runs every time that any testmethod is executed
            TestUtilsDomain.SetupTest();
            AllClassesAutoMapper.ClassDefCol = ClassDef.ClassDefs;
            var classDefCol = typeof(FakeMergeableParent).Assembly.MapClasses();
            ClassDef.ClassDefs.Add(classDefCol);
            BORegistry.DataAccessor = new DataAccessorInMemory();
        }

        [TestFixtureSetUp]
        public void TestFixtureSetup()
        {
            //Code that is executed before any test is run in this class. If multiple tests
            // are executed then it will still only be called once.
            TestUtilsDomain.SetupFixture();
        }

        [TearDown]
        public void TearDownTest()
        {
            //runs every time any testmethod is complete
        }
/*
        28/08/2010 Brett These Tests Are currently not required since the PendingChangeApproved is always true
 * for All Domain Objects (DM....). This is due to the fact that a user interface to view and accept changes has not yet been developed
        [Test]
        public void Test_UpdateAllBOsAsPendingChangeAccepted_ShouldUpPendingChangeApproveToTrue()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            IMergableBo solution = new DMSolution();
            //---------------Assert Precondition----------------
            Assert.IsFalse(solution.PendingChangeApproved, "Should start as false");
            //---------------Execute Test ----------------------
            traverser.UpdateAllBOsAsPendingChangeAccepted(solution);
            //---------------Test Result -----------------------
            Assert.IsTrue(solution.PendingChangeApproved, "Should update to true");
        }

        [Test]
        public void Test_UpdateAllBOsAsPendingChangeAccepted_WhenHasTwoAssembly_ShouldUpPendingChangeApproveToTrue()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            DMSolution solution = new DMSolution();
            var assembly1 = solution.Assemblies.CreateBusinessObject();
            var assembly2 = solution.Assemblies.CreateBusinessObject();
            IMergableBo mergableAssembly1 = assembly1;
            IMergableBo mergableAssembly2 = assembly2;
            //---------------Assert Precondition----------------
            //Assert.AreSame(solution, assembly.Solution);
            Assert.IsFalse(mergableAssembly1.PendingChangeApproved, "Assembly Should start as false");
            Assert.IsFalse(mergableAssembly2.PendingChangeApproved, "Assembly Should start as false");
            //---------------Execute Test ----------------------
            traverser.UpdateAllBOsAsPendingChangeAccepted(solution);
            //---------------Test Result -----------------------
            Assert.IsTrue(mergableAssembly1.PendingChangeApproved, "Assembly Should update to true");
            Assert.IsTrue(mergableAssembly2.PendingChangeApproved, "Assembly Should update to true");
        }
        [Test]
        public void Test_UpdateAllBOsAsPendingChangeAccepted_WhenHasTwoDMClasses_ShouldUpPendingChangeApproveToTrue()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            DMSolution solution = new DMSolution();
            var assembly = solution.Assemblies.CreateBusinessObject();
            var dmClass1 = assembly.Classes.CreateBusinessObject();
            var dmClass2 = assembly.Classes.CreateBusinessObject();
            IMergableBo mergeableClass1 = dmClass1;
            IMergableBo mergeableClass2 = dmClass2;
            //---------------Assert Precondition----------------
            //Assert.AreSame(solution, assembly.Solution);
            Assert.IsFalse(mergeableClass1.PendingChangeApproved, "Assembly Should start as false");
            Assert.IsFalse(mergeableClass2.PendingChangeApproved, "Assembly Should start as false");
            //---------------Execute Test ----------------------
            traverser.UpdateAllBOsAsPendingChangeAccepted(solution);
            //---------------Test Result -----------------------
            Assert.IsTrue(mergeableClass1.PendingChangeApproved, "Assembly Should update to true");
            Assert.IsTrue(mergeableClass2.PendingChangeApproved, "Assembly Should update to true");
        }
*/

        #region Mark For Delete

        [Test]
        public void Test_MarkForDeleteAllChangeTypeDelete_WhenChangeTypeDelete_ShouldMarkForDelete()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            IMergableBo solution = new DMSolution();
            solution.PendingChangeType = ChangeType.Delete;
            //---------------Assert Precondition----------------
            Assert.AreEqual(ChangeType.Delete, solution.PendingChangeType);
            Assert.IsFalse(solution.Status.IsDeleted);
            //---------------Execute Test ----------------------
            traverser.MarkForDeleteAllChangeTypeDelete(solution);
            //---------------Test Result -----------------------
            Assert.IsTrue(solution.Status.IsDeleted, "Should MarkForDelete");
        }     
        [Test]
        public void Test_MarkForDeleteAllChangeTypeDelete_WhenChangeTypeNotDelete_ShouldNotMarkForDelete()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            IMergableBo solution = new DMSolution();
            solution.PendingChangeType = ChangeType.Add;
            //---------------Assert Precondition----------------
            Assert.AreNotEqual(ChangeType.Delete, solution.PendingChangeType);
            Assert.IsFalse(solution.Status.IsDeleted);
            //---------------Execute Test ----------------------
            traverser.MarkForDeleteAllChangeTypeDelete(solution);
            //---------------Test Result -----------------------
            Assert.IsFalse(solution.Status.IsDeleted, "Should not MarkforDelete");
        }

        [Test]
        public void Test_MarkForDeleteAllChangeTypeDelete_WhenChildChangeTypeNotDelete_ShouldNotChildMarkForDelete()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            DMSolution solution = new DMSolution();
            var assembly1 = solution.Assemblies.CreateBusinessObject();
            var assembly2 = solution.Assemblies.CreateBusinessObject();
            IMergableBo mergableAssembly1 = assembly1;
            mergableAssembly1.PendingChangeType = ChangeType.Add;
            IMergableBo mergableAssembly2 = assembly2;
            mergableAssembly2.PendingChangeType = ChangeType.Add;
            //---------------Assert Precondition----------------
            Assert.AreNotEqual(ChangeType.Delete, mergableAssembly1.PendingChangeType);
            Assert.IsFalse(mergableAssembly1.Status.IsDeleted);
            Assert.AreNotEqual(ChangeType.Delete, mergableAssembly2.PendingChangeType);
            Assert.IsFalse(mergableAssembly2.Status.IsDeleted);
            //---------------Execute Test ----------------------
            traverser.MarkForDeleteAllChangeTypeDelete(solution);
            //---------------Test Result -----------------------
            Assert.IsFalse(mergableAssembly1.Status.IsDeleted, "Should not MarkforDelete");
            Assert.IsFalse(mergableAssembly2.Status.IsDeleted, "Should not MarkforDelete");
        }
        [Test]
        public void Test_MarkForDeleteAllChangeTypeDelete_WhenChildChangeTypeDelete_ShouldChildMarkForDelete()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            DMSolution solution = new DMSolution();
            var assembly1 = solution.Assemblies.CreateBusinessObject();
            var assembly2 = solution.Assemblies.CreateBusinessObject();
            IMergableBo mergableAssembly1 = assembly1;
            mergableAssembly1.PendingChangeType = ChangeType.Delete;
            IMergableBo mergableAssembly2 = assembly2;
            mergableAssembly2.PendingChangeType = ChangeType.Add;
            //---------------Assert Precondition----------------
            Assert.AreNotEqual(ChangeType.Delete, ((IMergableBo)solution).PendingChangeType);
            Assert.AreEqual(ChangeType.Delete, mergableAssembly1.PendingChangeType);
            Assert.IsFalse(mergableAssembly1.Status.IsDeleted);
            Assert.AreNotEqual(ChangeType.Delete, mergableAssembly2.PendingChangeType);
            Assert.IsFalse(mergableAssembly2.Status.IsDeleted);
            //---------------Execute Test ----------------------
            traverser.MarkForDeleteAllChangeTypeDelete(solution);
            //---------------Test Result -----------------------
            Assert.IsTrue(mergableAssembly1.Status.IsDeleted, "Should MarkforDelete");
            Assert.AreNotEqual(ChangeType.Delete, mergableAssembly2.PendingChangeType);
            Assert.IsFalse(mergableAssembly2.Status.IsDeleted, "Should not MarkforDelete");
        }

        [Test]
        public void Test_MarkForDeleteAllChangeTypeDelete_WhenHasTwoDMClasses_WhenApproveChangeFalse_ShouldNotMarkForDelete()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            DBDatabase database = new DBDatabase();
            var dbTable1 = database.Tables.CreateBusinessObject();
            var dbTable2 = database.Tables.CreateBusinessObject();
            IMergableBo mergeableClass1 = dbTable1;
            IMergableBo mergeableClass2 = dbTable2;
            mergeableClass1.PendingChangeType = ChangeType.Delete;
            mergeableClass1.PendingChangeApproved = false;
            mergeableClass2.PendingChangeType = ChangeType.Delete;
            mergeableClass2.PendingChangeApproved = false;
            //---------------Assert Precondition----------------
            Assert.AreEqual(ChangeType.Delete, mergeableClass1.PendingChangeType);
            Assert.IsFalse(mergeableClass1.PendingChangeApproved);
            Assert.IsFalse(mergeableClass1.Status.IsDeleted);
            Assert.AreEqual(ChangeType.Delete, mergeableClass2.PendingChangeType);
            Assert.IsFalse(mergeableClass2.PendingChangeApproved);
            Assert.IsFalse(mergeableClass2.Status.IsDeleted);
            //---------------Execute Test ----------------------
            traverser.MarkForDeleteAllChangeTypeDelete(database);
            //---------------Test Result -----------------------
            Assert.IsFalse(mergeableClass1.Status.IsDeleted, "Should Not MarkforDelete");
            Assert.AreEqual(ChangeType.Delete, mergeableClass2.PendingChangeType);
            Assert.IsFalse(mergeableClass2.Status.IsDeleted, "Should Not MarkforDelete");
        }

        [Test]
        public void Test_MarkForDeleteAllChangeTypeDelete_WhenSingleRelationship_ShouldMarkForDelete()
        {
            var traverser = new SolutionTraverser();
            UIView view = new UIView();
            view.UIGridInfo = new UIGridInfo();
            var mergableGrid = ((IMergableBo)view.UIGridInfo);
            mergableGrid.PendingChangeType = ChangeType.Delete;
            //---------------Assert Precondition----------------
            Assert.AreEqual(ChangeType.Delete, mergableGrid.PendingChangeType);
            Assert.IsFalse(view.UIGridInfo.Status.IsDeleted);
            //---------------Execute Test ----------------------
            traverser.MarkForDeleteAllChangeTypeDelete(view);
            //---------------Test Result -----------------------
            Assert.IsTrue(mergableGrid.Status.IsDeleted, "Should MarkforDelete");
        }
        [Test]
        public void Test_MarkForDeleteAllChangeTypeDelete_WhenSingleRelationshipWhenNotChangeTypeDel_ShouldNotMarkForDelete()
        {
            var traverser = new SolutionTraverser();
            FakeMergeableParent view = new FakeMergeableParent();
            view.FakeMergeableChild = new FakeMergeableChild();
            var mergableGrid = ((IMergableBo)view.FakeMergeableChild);
            mergableGrid.PendingChangeType = ChangeType.Add;
            //---------------Assert Precondition----------------
            Assert.AreNotEqual(ChangeType.Delete, mergableGrid.PendingChangeType);
            Assert.IsFalse(view.FakeMergeableChild.Status.IsDeleted);
            //---------------Execute Test ----------------------
            traverser.MarkForDeleteAllChangeTypeDelete(view);
            //---------------Test Result -----------------------
            Assert.IsFalse(mergableGrid.Status.IsDeleted, "Should Not MarkforDelete");
        }

        [Test]
        public void Test_MarkForDeleteAllChangeTypeDelete_WhenSingleRelationship_WhenPendingApprovedFalse_ShouldNotMarkForDelete()
        {
            var traverser = new SolutionTraverser();
            var parent = new FakeMergeableParent
                             {
                                 FakeMergeableChild = new FakeMergeableChild()
                             };
            var mergableChild = ((IMergableBo)parent.FakeMergeableChild);
            mergableChild.PendingChangeType = ChangeType.Delete;
            mergableChild.PendingChangeApproved = false;
            //---------------Assert Precondition----------------
            Assert.AreEqual(ChangeType.Delete, mergableChild.PendingChangeType);
            Assert.IsFalse(mergableChild.PendingChangeApproved);
            Assert.IsFalse(parent.FakeMergeableChild.Status.IsDeleted);
            //---------------Execute Test ----------------------
            traverser.MarkForDeleteAllChangeTypeDelete(parent);
            //---------------Test Result -----------------------
            Assert.IsFalse(parent.FakeMergeableChild.Status.IsDeleted, "Should Not MarkforDelete");
        }

        #endregion

        #region MarkChange Type

        [Test]
        public void Test_MarkChangeTypeNone_ShouldUpdateChangeType()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            IMergableBo view = new FakeMergeableParent();
            view.PendingChangeType = ChangeType.Add;
            //---------------Assert Precondition----------------
            Assert.AreNotEqual(ChangeType.None, view.PendingChangeType);
            //---------------Execute Test ----------------------
            traverser.MarkChangeTypeNone(view);
            //---------------Test Result -----------------------
            Assert.AreEqual(ChangeType.None, view.PendingChangeType);
        }

        [Test]
        public void Test_MarkChangeTypeNone_WhenSingleRel_ShouldUpdateChildChangeType()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            var view = new FakeMergeableParent();
            view.FakeMergeableChild = new FakeMergeableChild();
            var mergableGrid = ((IMergableBo)view.FakeMergeableChild);
            mergableGrid.PendingChangeType = ChangeType.Add;
            //---------------Assert Precondition----------------
            Assert.AreNotEqual(ChangeType.None, mergableGrid.PendingChangeType);
            //---------------Execute Test ----------------------
            traverser.MarkChangeTypeNone(view);
            //---------------Test Result -----------------------
            Assert.AreEqual(ChangeType.None, mergableGrid.PendingChangeType);
            Assert.AreEqual(ChangeType.None, ((IMergableBo)view).PendingChangeType);
        }
        [Test]
        public void Test_MarkChangeTypeNone_WhenHasTwoDMClasses_ShouldUpdateChildChangeType()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            DMSolution solution = new DMSolution();
            var assembly = solution.Assemblies.CreateBusinessObject();
            var dmClass1 = assembly.Classes.CreateBusinessObject();
            var dmClass2 = assembly.Classes.CreateBusinessObject();
            IMergableBo mergeableClass1 = dmClass1;
            IMergableBo mergeableClass2 = dmClass2;
            mergeableClass1.PendingChangeType = ChangeType.Delete;
            mergeableClass2.PendingChangeType = ChangeType.Add;
            //---------------Assert Precondition----------------
            //Assert.AreSame(solution, assembly.Solution);
            Assert.AreNotEqual(ChangeType.None, mergeableClass1.PendingChangeType);
            Assert.AreNotEqual(ChangeType.None, mergeableClass2.PendingChangeType);
            //---------------Execute Test ----------------------
            traverser.MarkChangeTypeNone(solution);
            //---------------Test Result -----------------------
            Assert.AreEqual(ChangeType.None, mergeableClass1.PendingChangeType);
            Assert.AreEqual(ChangeType.None, mergeableClass2.PendingChangeType);
        }

        [Test]
        public void Test_MarkChangeTypeAdd_WhenSingleRel_ShouldUpdateChildChangeType()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            var view = new FakeMergeableParent();
            view.FakeMergeableChild = new FakeMergeableChild();
            var mergableGrid = ((IMergableBo)view.FakeMergeableChild);
            mergableGrid.PendingChangeType = ChangeType.None;
            //---------------Assert Precondition----------------
            Assert.AreEqual(ChangeType.None, mergableGrid.PendingChangeType);
            //---------------Execute Test ----------------------
            traverser.MarkChangeTypeAdd(view);
            //---------------Test Result -----------------------
            Assert.AreEqual(ChangeType.Add, mergableGrid.PendingChangeType);
            Assert.AreEqual(ChangeType.Add, ((IMergableBo)view).PendingChangeType);
        }

        [Test]
        public void Test_MarkChangeTypeAdd_WhenHasTwoDMClasses_ShouldUpdateChildChangeType()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            DMSolution solution = new DMSolution();
            var assembly = solution.Assemblies.CreateBusinessObject();
            var dmClass1 = assembly.Classes.CreateBusinessObject();
            var dmClass2 = assembly.Classes.CreateBusinessObject();
            IMergableBo mergeableClass1 = dmClass1;
            IMergableBo mergeableClass2 = dmClass2;
            mergeableClass1.PendingChangeType = ChangeType.None;
            mergeableClass2.PendingChangeType = ChangeType.None;
            //---------------Assert Precondition----------------
            //Assert.AreSame(solution, assembly.Solution);
            Assert.AreEqual(ChangeType.None, mergeableClass1.PendingChangeType);
            Assert.AreEqual(ChangeType.None, mergeableClass2.PendingChangeType);
            //---------------Execute Test ----------------------
            traverser.MarkChangeTypeAdd(solution);
            //---------------Test Result -----------------------
            Assert.AreEqual(ChangeType.Add, mergeableClass1.PendingChangeType);
            Assert.AreEqual(ChangeType.Add, mergeableClass2.PendingChangeType);
        }

        #endregion

        #region Set PendingChangeApproved

        [Test]
        public void Test_SetPendingChangeApproved_ToTrue_ShouldUpdatePendingChangeApproved_True()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            IMergableBo view = new FakeMergeableParent();
            view.PendingChangeApproved = false;
            //---------------Assert Precondition----------------
            Assert.IsFalse(view.PendingChangeApproved);
            //---------------Execute Test ----------------------
            traverser.SetPendingChangeApproved(view, true);
            //---------------Test Result -----------------------
            Assert.IsTrue(view.PendingChangeApproved);
        }

        [Test]
        public void Test_SetPendingChangeApproved_ToFalse_ShouldUpdatePendingChangeApproved_False()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            IMergableBo view = new FakeMergeableParent();
            view.PendingChangeApproved = true;
            view.PendingChangeType = ChangeType.Update;
            //---------------Assert Precondition----------------
            Assert.IsTrue(view.PendingChangeApproved);
            //---------------Execute Test ----------------------
            traverser.SetPendingChangeApproved(view, false);
            //---------------Test Result -----------------------
            Assert.IsFalse(view.PendingChangeApproved);
        }

        [TestCase(false, true)]
        [TestCase(true, false)]
        public void Test_SetPendingChangeApproved_WhenSingleRel_ShouldUpdateChildPendingChangeApproved_True(bool initialApproved, bool expectedApproved)
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            var parent = new FakeMergeableParent
                           {
                               FakeMergeableChild = new FakeMergeableChild()
                           };
            var mergeableSingeChild = ((IMergableBo)parent.FakeMergeableChild);
            parent.PendingChangeApproved = initialApproved;
            mergeableSingeChild.PendingChangeApproved = initialApproved;
            //---------------Assert Precondition----------------
            Assert.AreEqual(initialApproved, parent.PendingChangeApproved);
            Assert.AreEqual(initialApproved, mergeableSingeChild.PendingChangeApproved);
            //---------------Execute Test ----------------------
            traverser.SetPendingChangeApproved(parent, expectedApproved);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedApproved, parent.PendingChangeApproved, "Parent should have been changed to '" + expectedApproved + "'");
            Assert.AreEqual(expectedApproved, mergeableSingeChild.PendingChangeApproved, "SingleChild should have been changed to '" + expectedApproved + "'");
        }

        [TestCase(false, true)]
        [TestCase(true, false)]
        public void Test_SetPendingChangeApproved_WhenHasTwoChildren_ShouldUpdateChildPendingChangeApproved_True(bool initialApproved, bool expectedApproved)
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverser();
            var parent = GetTestBuilderParent()
                .WithTwo(parent1 => parent1.FakeCompositionMergeables)
                .CreateValidBusinessObject();

            parent.PendingChangeApproved = initialApproved;
            parent.PendingChangeType = ChangeType.Update;
            var mergeableChild = parent.FakeCompositionMergeables[0];
            mergeableChild.PendingChangeApproved = initialApproved;
            mergeableChild.PendingChangeType = ChangeType.Update;
            var mergeableChild2 = parent.FakeCompositionMergeables[1];
            mergeableChild2.PendingChangeApproved = initialApproved;
            mergeableChild2.PendingChangeType = ChangeType.Update;

            //---------------Assert Precondition----------------
            Assert.AreEqual(initialApproved, parent.PendingChangeApproved);
            Assert.AreEqual(initialApproved, mergeableChild.PendingChangeApproved);
            Assert.AreEqual(initialApproved, mergeableChild2.PendingChangeApproved);
            //---------------Execute Test ----------------------
            traverser.SetPendingChangeApproved(parent, expectedApproved);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedApproved, parent.PendingChangeApproved, "Parent should have been changed to '" + expectedApproved + "'");
            Assert.AreEqual(expectedApproved, mergeableChild.PendingChangeApproved, "MultipleChild should have been changed to '" + expectedApproved + "'");
            Assert.AreEqual(expectedApproved, mergeableChild2.PendingChangeApproved, "MultipleChild should have been changed to '" + expectedApproved + "'");
        }

        [Test]
        public void Test_SetPendingChangeApproved_WhenBOIsNull_ShouldDoNothing()
        {
            var traverser = new SolutionTraverser();
            FakeMergeableParent parent = null;

            //---------------Assert Precondition----------------
            //Assert.AreSame(solution, assembly.Solution);
            Assert.IsNull(parent);
            //---------------Execute Test ----------------------
            traverser.SetPendingChangeApproved(parent, true);
            //---------------Test Result -----------------------
            Assert.IsTrue(true, "Should get here");
        }


        [Test]
        public void Test_SetParentPendingChangeApproved_SingleChild_ShouldUpdateChildPendingChangeApproved()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverserSpy();
            var parent = new FakeMergeableParent
            {
                PendingChangeApproved = false
            };

            var fakeMergeableChild = new FakeMergeableChild
            {
                PendingChangeApproved = false
            };

            bool initialPendingChangeApproved = false;
            bool expectedPendingChangeApproved = true;
            parent.FakeMergeableChild = fakeMergeableChild;
            //---------------Assert Precondition----------------
            Assert.AreEqual(initialPendingChangeApproved, fakeMergeableChild.PendingChangeApproved);
            //---------------Execute Test ----------------------
            traverser.CallSetParentPendingChangeApproved(fakeMergeableChild, true);
            //---------------Test Result -----------------------

            Assert.AreEqual(expectedPendingChangeApproved, fakeMergeableChild.PendingChangeApproved, "fakeMergeableChild.PendingChangeApproved should be '" + expectedPendingChangeApproved + "'");
        }

        [Test]
        public void Test_SetParentPendingChangeApproved_SingleChild_ShouldUpdateParentPendingChangeApproved()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverserSpy();
            var parent = new FakeMergeableParent
                         {
                             PendingChangeApproved = false
                         };

            var fakeMergeableChild = new FakeMergeableChild
                                     {
                                         PendingChangeApproved = false
                                     };

            parent.FakeMergeableChild = fakeMergeableChild;
            
            bool initialPendingChangeApproved = false;
            bool expectedPendingChangeApproved = true;
            //---------------Assert Precondition----------------
            Assert.AreEqual(initialPendingChangeApproved, fakeMergeableChild.PendingChangeApproved);
            Assert.AreEqual(initialPendingChangeApproved, parent.PendingChangeApproved);
            //---------------Execute Test ----------------------
            traverser.CallSetParentPendingChangeApproved(fakeMergeableChild, true);
            //---------------Test Result -----------------------

            Assert.AreEqual(expectedPendingChangeApproved, fakeMergeableChild.PendingChangeApproved, "fakeMergeableChild.PendingChangeApproved should be '" + expectedPendingChangeApproved + "'");
            Assert.AreEqual(expectedPendingChangeApproved, parent.PendingChangeApproved, "parent.PendingChangeApproved should be '" + expectedPendingChangeApproved + "'");
        }


        [Test]
        public void Test_SetParentPendingChangeApproved_TwoChildrenOneSetFalse_ShouldUpdateParentPendingChangeApproved_True()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverserSpy();
            var parent = GetTestBuilderParent()
                .WithTwo(parent1 => parent1.FakeCompositionMergeables)
                .CreateValidBusinessObject();

            bool initialApproved = true;
            bool changedApproved = false;
            bool expectedApproved = true;

            parent.PendingChangeApproved = initialApproved;
            var mergeableChild = parent.FakeCompositionMergeables[0];
            mergeableChild.PendingChangeApproved = initialApproved;
            var mergeableChild2 = parent.FakeCompositionMergeables[1];
            mergeableChild2.PendingChangeApproved = initialApproved;
            
            //---------------Assert Precondition----------------
            Assert.AreEqual(initialApproved, mergeableChild.PendingChangeApproved);
            Assert.AreEqual(initialApproved, mergeableChild2.PendingChangeApproved);
            Assert.AreEqual(initialApproved, parent.PendingChangeApproved);
            //---------------Execute Test ----------------------
            traverser.CallSetParentPendingChangeApproved(mergeableChild, changedApproved);
            
            //---------------Test Result -----------------------
            Assert.AreEqual(changedApproved, mergeableChild.PendingChangeApproved, "MultipleChild should have been changed to '" + changedApproved + "'");
            Assert.AreEqual(initialApproved, mergeableChild2.PendingChangeApproved, "MultipleChild should have been changed to '" + initialApproved + "'");
            Assert.AreEqual(expectedApproved, parent.PendingChangeApproved, "Parent should not have been changed from '" + expectedApproved + "'");

        }

        private BOTestFactory<FakeMergeableParent> GetTestBuilderParent()
        {
            return GetTestBuilder<FakeMergeableParent>();
        }

        private static BOTestFactory<T> GetTestBuilder<T>() where T : class, IBusinessObject
        {
            return BOTestFactoryRegistry.Instance.Resolve<T>();
        }
        #endregion


        #region Get Parent

        [Test]
        public void Test_GetParent_BoThatIsNull_ReturnsNull()
        {
            //---------------Set up test pack-------------------
            SolutionTraverserSpy solutionTraverser = new SolutionTraverserSpy();
            IMergableBo bo = null;
            //---------------Assert Precondition----------------
            Assert.IsNull(bo);
            //---------------Execute Test ----------------------
            var parentBO = solutionTraverser.CallGetParent(bo);
            //---------------Test Result -----------------------
            Assert.IsNull(parentBO);
        }

        [Test]
        public void Test_GetParent_BoWithNoParent_ShouldReturn_Null()
        {
            //---------------Set up test pack-------------------
            SolutionTraverserSpy traverserSpy = new SolutionTraverserSpy();
            IMergableBo bo = new FakeMergeableParent();
            //---------------Assert Precondition----------------
            var relationships = bo.Relationships.Where(IsAssociation);
            Assert.AreEqual(0,relationships.Count(),"Should not be any association relationships");
            //---------------Execute Test ----------------------
            var parentBO = traverserSpy.CallGetParent(bo);
            //---------------Test Result -----------------------
            Assert.IsNull(parentBO);
        }

        [Test]
        public void Test_GetParent_BoWithParent_ShouldReturn_ParentBO()
        {
            //---------------Set up test pack-------------------
            SolutionTraverserSpy traverserSpy = new SolutionTraverserSpy();
            FakeMergeableChild bo = new FakeMergeableChild();
            FakeMergeableParent expectedParent = new FakeMergeableParent();
            bo.FakeMergeableParentReverse = expectedParent;
           //---------------Assert Precondition----------------

            Assert.IsInstanceOf<IMergableBo>(bo.FakeMergeableParentReverse);
            Assert.AreSame(expectedParent, bo.FakeMergeableParentReverse);
            //---------------Execute Test ----------------------
            var returnedParent = traverserSpy.CallGetParent(bo);
            //---------------Test Result -----------------------
            Assert.IsInstanceOf<IMergableBo>(returnedParent);
            Assert.AreSame(expectedParent,  returnedParent);
        }

        [Test]
        public void Test_GetParent_WhenHasRelatedBOButIsNotParent_ShouldReturn_Null()
        {
            //---------------Set up test pack-------------------
            FakeBO2WithOneToOneAssociationRel.LoadDefaultClassDef();
            FakeBOWithOneToOneAssociationRel.LoadDefaultClassDef();
            SolutionTraverserSpy traverserSpy = new SolutionTraverserSpy();
            var bo = new FakeBOWithOneToOneAssociationRel();
            var parentBO = new FakeBO2WithOneToOneAssociationRel();
            parentBO.FakeMergeableRel = bo;
           //---------------Assert Precondition----------------
            Assert.IsInstanceOf<IMergableBo>(bo.FakeMergeableParentReverse);
            Assert.AreSame(parentBO, bo.FakeMergeableParentReverse);
            //---------------Execute Test ----------------------
            var returnedParent = traverserSpy.CallGetParent(parentBO);
            //---------------Test Result -----------------------
            Assert.IsNull(returnedParent);
        }
/*



        private static bool IsOneToMany(IRelationship relationship)
        {
            return relationship.RelationshipDef.IsOneToMany;
        }
        private static bool IsOneToOne(IRelationship relationship)
        {
            return relationship is ISingleRelationship;
        }*/

        private static bool IsAssociation(IRelationship relationship)
        {
            return relationship.RelationshipDef.RelationshipType == RelationshipType.Association;
        }


        #endregion

        #region Has Pending Changes

                [Test]
        public void Test_HasPendingChanges_GivenAMergableBOWithPendingChangesApproved_ShouldReturn_True()
        {
            //---------------Set up test pack-------------------

            const bool initialPendingChangeApproved = true;
            
            SolutionTraverserSpy traverserSpy = new SolutionTraverserSpy();
            var fakeMergeableChild = new FakeMergeableChild
            {
                PendingChangeApproved = initialPendingChangeApproved
            };


            //---------------Assert Precondition----------------
            Assert.That(fakeMergeableChild.PendingChangeApproved, Is.True);
            //---------------Execute Test ----------------------
            bool callHasPendingChanges = traverserSpy.CallHasPendingChanges(fakeMergeableChild);
            //---------------Test Result -----------------------
            Assert.That(callHasPendingChanges,Is.True);
        }
       
        [Test]
        public void Test_HasPendingChanges_ParentMergableBO_True_WithChildMergableBO_True_ShouldReturn_True()
        {
            //---------------Set up test pack-------------------

            const bool initialPendingChangeApproved = true;
            
            SolutionTraverserSpy traverserSpy = new SolutionTraverserSpy();
            var parent = new FakeMergeableParent
            {
                PendingChangeApproved = initialPendingChangeApproved
            };

            var fakeMergeableChild = new FakeMergeableChild
            {
                PendingChangeApproved = initialPendingChangeApproved
            };

            parent.FakeMergeableChild = fakeMergeableChild;

            //---------------Assert Precondition----------------
            Assert.That(fakeMergeableChild.PendingChangeApproved, Is.True);
            //---------------Execute Test ----------------------
            bool callHasPendingChanges = traverserSpy.CallHasPendingChanges(parent);
            //---------------Test Result -----------------------
            Assert.That(callHasPendingChanges,Is.True);
        }

        [Test]
        public void Test_HasPendingChanges_WhenParentMergableBOTrue_WithTwoChildMergableBO_OneTrue_ShouldReturn_True()
        {
            //---------------Set up test pack-------------------
            var traverser = new SolutionTraverserSpy();
            var parent = GetTestBuilderParent()
                .WithTwo(parent1 => parent1.FakeCompositionMergeables)
                .CreateValidBusinessObject();

            bool initialApproved = true;
            bool changedNotApproved = false;

            parent.PendingChangeApproved = initialApproved;
            var mergeableChild = parent.FakeCompositionMergeables[0];
            mergeableChild.PendingChangeApproved = initialApproved;
            var mergeableChild2 = parent.FakeCompositionMergeables[1];
            mergeableChild2.PendingChangeApproved = changedNotApproved;


            //---------------Assert Precondition----------------
            Assert.AreEqual(initialApproved, parent.PendingChangeApproved);
            Assert.AreEqual(initialApproved, mergeableChild.PendingChangeApproved);
            Assert.AreEqual(changedNotApproved, mergeableChild2.PendingChangeApproved);
            //---------------Execute Test ----------------------
            bool callHasPendingChanges = traverser.CallHasPendingChanges(parent);

            //---------------Test Result -----------------------
            Assert.That(callHasPendingChanges, Is.True);

        }



        #endregion

    }

    internal class SolutionTraverserSpy : SolutionTraverser
    {
        public IMergableBo CallGetParent(IMergableBo child)
        {
            return base.GetParent(child);
        }

        public bool CallHasPendingChanges(IMergableBo currentBO)
        {
            return HasPendingChangesApproved(currentBO);
        }

        public void CallSetParentPendingChangeApproved(FakeMergeableChild mergeableChild, bool changedApproved)
        {
            base.SetParentPendingChangeApproved(mergeableChild, changedApproved);
        }
    }




}